package com.xdf.springboot.filter;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class CosSecurityUtil {

    private final Logger log = LoggerFactory.getLogger(CosSecurityUtil.class);

    @Resource
    private CosSecurityProperties cosSecurityProperties;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 校验请求有效时间
     *
     * @param time
     * @return
     */
    public void validTimestamp(String time) throws Exception {
        if (StrUtil.isEmpty(time)) {
            throw new BusinessException(ExceptionEnum.TIMESTAMP_INVALID.getCode(), ExceptionEnum.TIMESTAMP_INVALID.getErrMsg());
        }

        if (!Pattern.matches(CosSecurityConstants.PATTERN_TIMESTAMP, time)) {
            throw new BusinessException(ExceptionEnum.TIMESTAMP_INVALID.getCode(), ExceptionEnum.TIMESTAMP_INVALID.getErrMsg());
        }


        Long timestamp = Long.valueOf(time);
        //服务器UTC时间
        LocalDateTime utcLocalDateTime = Instant.now().atZone(ZoneId.of("UTC")).toLocalDateTime();

        LocalDateTime severalMinutesBefore = LocalDateTimeUtil.offset(utcLocalDateTime, -10L, ChronoUnit.MINUTES);
        LocalDateTime severalMinutesAfter = LocalDateTimeUtil.offset(utcLocalDateTime, 10L, ChronoUnit.MINUTES);

        if (timestamp > LocalDateTimeUtil.toEpochMilli(severalMinutesBefore) && timestamp < LocalDateTimeUtil.toEpochMilli(severalMinutesAfter)) {
            //do nothing
        } else {
            //签名超时
            throw new BusinessException(ExceptionEnum.SIGN_TIMEOUT.getCode(), ExceptionEnum.SIGN_TIMEOUT.getErrMsg());
        }
    }


    /**
     * 校验nonce，并保存至redis中
     *
     * @param nonce
     * @param request
     * @return
     */
    public void validAndSaveNonce(String nonce, HttpServletRequest request) throws Exception {
        if (StrUtil.isEmpty(nonce)) {
            throw new BusinessException(ExceptionEnum.NONCE_INVALID.getCode(), ExceptionEnum.NONCE_INVALID.getErrMsg());
        }

        if (!Pattern.matches(CosSecurityConstants.PATTERN_NONCE, nonce)) {
            throw new BusinessException(ExceptionEnum.NONCE_INVALID.getCode(), ExceptionEnum.NONCE_INVALID.getErrMsg());
        }

        String remoteHost = request.getRemoteHost();
        String method = request.getMethod();
        String requestUrl = request.getRequestURL().toString();
        JSONObject nonceRedisValue = new JSONObject();
        nonceRedisValue.put("remoteHost", remoteHost);
        nonceRedisValue.put("method", method);
        nonceRedisValue.put("requestUrl", requestUrl);

        String nonceRedisKey = CosSecurityConstants.PREFIX_NONCE_KEY + nonce;
        Boolean hasKey = stringRedisTemplate.hasKey(nonceRedisKey);
        if (null != hasKey && hasKey) {
            throw new BusinessException(ExceptionEnum.REQUEST_REPEAT.getCode(), ExceptionEnum.REQUEST_REPEAT.getErrMsg());
        }
        RBucket<String> nonceBucket = redissonClient.getBucket(nonceRedisKey);
        boolean isSuccess = nonceBucket.trySet(nonceRedisValue.toJSONString(), 10L, TimeUnit.MINUTES);
        if (!isSuccess) {
            throw new BusinessException(ExceptionEnum.REQUEST_REPEAT.getCode(), ExceptionEnum.REQUEST_REPEAT.getErrMsg());
        }
    }

    /**
     * 校验POST请求的sign
     *
     * @param sign
     * @param timestamp
     * @param nonce
     * @throws Exception
     */
    public void validPostSign(String sign, Long timestamp, String nonce, BodyReaderHttpServletRequestWrapper request) throws Exception {
        if (StrUtil.isEmpty(sign)) {
            throw new BusinessException(ExceptionEnum.SIGN_INVALID.getCode(), ExceptionEnum.SIGN_INVALID.getErrMsg());
        }

        if (!Pattern.matches(CosSecurityConstants.PATTERN_SIGN, sign)) {
            throw new BusinessException(ExceptionEnum.SIGN_INVALID.getCode(), ExceptionEnum.SIGN_INVALID.getErrMsg());
        }

        //取请求body体的md5摘要
        String reqBody = request.getBodyString();
        log.info("【安全基线-POST请求签名校验】requestBody=[{}]", reqBody);
        String body = DigestUtils.md5Hex(reqBody);
        //计算规则：sign=md5(timestamp+nonce+body+key)
        String signString = "timestamp=" + timestamp + "&nonce=" + nonce + "&body=" + body +"&key=" + cosSecurityProperties.getSecurityCode();
        log.info("验签字符串拼接结果为：{}",signString);
        String serverSign = DigestUtils.md5Hex(signString);
        log.info("【安全基线-POST请求签名校验】前端sign=[{}]，后端计算body=[{}],后端sign=[{}]", sign, body, serverSign);
        if (!StrUtil.equals(sign.toLowerCase(), serverSign.toLowerCase())) {
            throw new BusinessException(ExceptionEnum.SIGN_INVALID.getCode(), ExceptionEnum.SIGN_INVALID.getErrMsg());
        }
    }
    public static void main(String[] args){
        String reqBody = "";
        String body = DigestUtils.md5Hex(reqBody);
        LocalDateTime utcLocalDateTime = Instant.now().atZone(ZoneId.of("UTC")).toLocalDateTime();
        Long timestamp = LocalDateTimeUtil.toEpochMilli(utcLocalDateTime);
        System.out.println(timestamp);
        String nonce = "eb8f198d36b3edcb913ade2506707631";
        //计算规则：sign=md5(timestamp+nonce+body+key)
        String signString = "timestamp=" + timestamp + "&nonce=" + nonce + "&body=" + body +"&key=" + "RETAILCLOUD@HUA123";
        String serverSign = DigestUtils.md5Hex(signString);
        System.out.println(serverSign);
    }
}